import cv2
import numpy as np
import pyautogui
import pydirectinput
from ultralytics import YOLO
import time
import random

# 禁用PyAutoGUI的安全特性
pyautogui.FAILSAFE = False

# 游戏区域设置
x = 0
y = 87
width = 842
height = 570

# 移动方向常量
UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'

# 新增：每次移动后的停顿时间
MOVE_PAUSE_TIME = 0.2

# 移动策略相关变量
current_direction = None  # 当前移动方向
opposite_direction = {  # 方向的反方向映射
    UP: DOWN,
    DOWN: UP,
    LEFT: RIGHT,
    RIGHT: LEFT
}
consecutive_blocked_count = 0  # 连续被阻挡的计数
min_steps_in_direction = 5  # 至少朝一个方向走的步数
current_step_count = 0  # 当前方向已走的步数

# 新增：主方向相关变量
main_direction = None  # 当前主方向
main_direction_steps = 0  # 沿着主方向移动的步数
MIN_MAIN_DIRECTION_STEPS = 20  # 至少保持主方向的步数
MAIN_DIRECTION_CHANGE_PROB = 0.02  # 随机改变主方向的概率

# 移动序列检测相关变量
move_sequence = []  # 记录最近的移动方向
SEQUENCE_LENGTH = 6  # 检测序列长度
PATTERN_UP_DOWN = [UP, UP, DOWN, DOWN]  # 上上下下模式
PATTERN_LEFT_RIGHT = [LEFT, LEFT, RIGHT, RIGHT]  # 左左右右模式
pattern_detected = False  # 是否检测到模式
pattern_counter = 0  # 模式触发后的计数
PATTERN_MOVE_COUNT = 10  # 模式触发后执行的移动次数

# 速度控制相关变量
normal_move_interval = 1  # 正常移动命令间隔(秒)
obstacle_move_interval = 1   # 遇到障碍物时的移动命令间隔(秒)

# 方向的垂直方向映射
perpendicular_directions = {
    UP: [LEFT, RIGHT],  # 上方向的垂直方向是左和右
    DOWN: [LEFT, RIGHT],  # 下方向的垂直方向是左和右
    LEFT: [UP, DOWN],  # 左方向的垂直方向是上和下
    RIGHT: [UP, DOWN]  # 右方向的垂直方向是上和下
}

# 新增：记录上次被阻挡的方向
last_blocked_direction = None

# 定义一个安全距离，避免角色太靠近障碍物边缘
SAFETY_DISTANCE = 5
# 新增：FPS控制参数
TARGET_FPS = 30  # 目标FPS
FRAME_DELAY = 1 / TARGET_FPS  # 每帧目标延迟时间

def capture_screen():
    """捕获游戏区域屏幕截图"""
    screenshot = pyautogui.screenshot(region=(x, y, width, height))
    return cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)


def detect_objects(image, model):
    """使用YOLOv8模型检测游戏中的物体"""
    return model(image, verbose=False)  # 关闭详细输出以提高速度


def get_detection_info(results):
    """解析检测结果，提取各类物体的位置信息"""
    detection_info = {
        '岩浆': [],
        '自己': [],
        '石块': [],
        '怪兽': []
    }

    for r in results:
        boxes = r.boxes
        for box in boxes:
            class_id = r.names[int(box.cls)]
            x1, y1, x2, y2 = box.xyxy[0].cpu().numpy().astype(int)
            if class_id in detection_info:
                detection_info[class_id].append((x1, y1, x2, y2))

    return detection_info


def get_self_center(self_positions):
    """获取自己的中心位置"""
    if not self_positions:
        return None

    # 假设第一个检测到的是自己
    x1, y1, x2, y2 = self_positions[0]
    return ((x1 + x2) // 2, (y1 + y2) // 2)


def is_direction_blocked(self_center, obstacles, direction):
    """判断指定方向是否被障碍物阻挡"""
    if self_center is None:
        return True

    cx, cy = self_center

    # 获取自己的边界框（假设角色大小为10x10）
    self_size = 10
    self_x1 = cx - self_size // 2
    self_y1 = cy - self_size // 2
    self_x2 = cx + self_size // 2
    self_y2 = cy + self_size // 2

    for x1, y1, x2, y2 in obstacles:
        if direction == RIGHT:
            # 检查右边是否有障碍物
            if (self_y2 + SAFETY_DISTANCE >= y1 and self_y1 - SAFETY_DISTANCE <= y2) and (
                    self_x2 + SAFETY_DISTANCE >= x1):
                return True
        elif direction == LEFT:
            # 检查左边是否有障碍物
            if (self_y2 + SAFETY_DISTANCE >= y1 and self_y1 - SAFETY_DISTANCE <= y2) and (
                    self_x1 - SAFETY_DISTANCE <= x2):
                return True
        elif direction == UP:
            # 检查上边是否有障碍物
            if (self_x2 + SAFETY_DISTANCE >= x1 and self_x1 - SAFETY_DISTANCE <= x2) and (
                    self_y1 - SAFETY_DISTANCE <= y2):
                return True
        elif direction == DOWN:
            # 检查下边是否有障碍物
            if (self_x2 + SAFETY_DISTANCE >= x1 and self_x1 - SAFETY_DISTANCE <= x2) and (
                    self_y2 + SAFETY_DISTANCE >= y1):
                return True

    return False


def detect_pattern(sequence):
    """检测移动序列中是否包含特定模式"""
    if len(sequence) < SEQUENCE_LENGTH:
        return None

    # 检查上上下下模式
    if sequence[-len(PATTERN_UP_DOWN):] == PATTERN_UP_DOWN:
        return [LEFT, RIGHT]  # 触发左右移动

    # 检查左左右右模式
    if sequence[-len(PATTERN_LEFT_RIGHT):] == PATTERN_LEFT_RIGHT:
        return [UP, DOWN]  # 触发上下移动

    return None


def choose_direction(self_center, obstacles):
    """根据障碍物位置选择下一步移动方向"""
    global current_direction, consecutive_blocked_count, current_step_count
    global move_sequence, pattern_detected, pattern_counter
    global main_direction, main_direction_steps
    global move_interval, last_blocked_direction

    # 检查是否需要执行模式触发后的移动
    if pattern_detected:
        pattern_counter += 1

        # 模式触发后执行一定次数的移动
        if pattern_counter <= PATTERN_MOVE_COUNT:
            # 在左右之间切换
            if pattern_counter % 2 == 1:
                return pattern_detected[0]  # 第一次移动使用第一个方向
            else:
                return pattern_detected[1]  # 第二次移动使用第二个方向
        else:
            # 模式执行完毕，重置状态
            pattern_detected = False
            pattern_counter = 0

    # 如果还没有主方向，随机选择一个
    if main_direction is None:
        main_direction = random.choice([UP, DOWN, LEFT, RIGHT])
        main_direction_steps = 0
        print(f"新主方向: {main_direction}")

    # 有几率随机改变主方向
    if main_direction_steps >= MIN_MAIN_DIRECTION_STEPS and random.random() < MAIN_DIRECTION_CHANGE_PROB:
        possible_directions = [d for d in [UP, DOWN, LEFT, RIGHT] if d != main_direction]
        main_direction = random.choice(possible_directions)
        main_direction_steps = 0
        print(f"随机改变主方向: {main_direction}")

    # 如果还没有方向，或者当前方向与主方向不一致，设置为主方向
    if current_direction is None or current_direction != main_direction:
        current_direction = main_direction
        current_step_count = 0
        consecutive_blocked_count = 0
        return current_direction

    # 更新移动序列和主方向步数
    move_sequence.append(current_direction)
    if len(move_sequence) > SEQUENCE_LENGTH:
        move_sequence.pop(0)
    main_direction_steps += 1

    # 检测是否有模式触发
    pattern = detect_pattern(move_sequence)
    if pattern:
        print(f"检测到模式! 触发: {pattern}")
        pattern_detected = pattern
        pattern_counter = 0
        return pattern[0]  # 开始执行模式移动

    # 正常移动逻辑
    all_obstacles = obstacles['岩浆'] + obstacles['石块']
    is_blocked = is_direction_blocked(self_center, all_obstacles, current_direction)

    # 如果被阻挡
    if is_blocked:
        consecutive_blocked_count += 1
        last_blocked_direction = current_direction

        # 如果连续被阻挡超过1次，随机选择一个新方向
        if consecutive_blocked_count >= 1:
            possible_directions = [d for d in [UP, DOWN, LEFT, RIGHT] if
                                   d != current_direction and d != last_blocked_direction]
            if not possible_directions:
                possible_directions = [d for d in [UP, DOWN, LEFT, RIGHT] if d != current_direction]

            current_direction = random.choice(possible_directions)

            # 如果主方向被阻挡，也更新主方向
            if current_direction != main_direction:
                main_direction = current_direction
                print(f"主方向被阻挡，切换主方向: {main_direction}")

            current_step_count = 0
            consecutive_blocked_count = 0
            print(f"连续被阻挡，切换方向: {current_direction}")
            move_interval = obstacle_move_interval  # 遇到障碍物时，设置移动间隔为慢速度
            return current_direction

        # 尝试选择垂直方向
        perpendicular_dirs = perpendicular_directions[current_direction]
        available_dirs = []

        for direction in perpendicular_dirs:
            if not is_direction_blocked(self_center, all_obstacles, direction):
                available_dirs.append(direction)

        if available_dirs:
            current_direction = random.choice(available_dirs)
            print(f"遇到障碍物，选择垂直方向: {current_direction}")
            move_interval = obstacle_move_interval  # 遇到障碍物时，设置移动间隔为慢速度
            return current_direction
        else:
            # 如果两个垂直方向都被阻挡，选择反方向
            current_direction = opposite_direction[current_direction]
            print(f"两个垂直方向都被阻挡，选择反方向: {current_direction}")
            move_interval = obstacle_move_interval  # 遇到障碍物时，设置移动间隔为慢速度
            return current_direction

    else:
        # 如果没有被阻挡，继续当前方向
        consecutive_blocked_count = 0
        current_step_count += 1

        # 每走一定步数，有几率随机改变方向（但保持主方向不变）
        if current_step_count >= min_steps_in_direction and random.random() < 0.1:
            # 优先选择与主方向一致的方向
            if main_direction in [LEFT, RIGHT]:
                possible_directions = [LEFT, RIGHT]
            else:
                possible_directions = [UP, DOWN]

            current_direction = random.choice(possible_directions)
            current_step_count = 0
            print(f"随机改变方向: {current_direction} (保持主方向: {main_direction})")

        move_interval = normal_move_interval  # 没有遇到障碍物，设置移动间隔为正常速度
        return current_direction


def move(direction):
    """执行移动操作"""
    if direction is None:
        return

    # 释放所有方向键
    pydirectinput.keyUp(UP)
    pydirectinput.keyUp(DOWN)
    pydirectinput.keyUp(LEFT)
    pydirectinput.keyUp(RIGHT)

    # 按下选定的方向键
    pydirectinput.keyDown(direction)
    print(f"移动方向: {direction}")

    # 停顿一段时间，等待游戏响应
    time.sleep(MOVE_PAUSE_TIME)

    # 释放按键
    pydirectinput.keyUp(direction)


def main():
    # 加载模型
    model = YOLO('best.pt')

    # 初始化时间计数器
    last_time = time.time()

    # 记录上一次检测的结果
    last_detection = None

    try:
        while True:
            # 捕获屏幕截图
            screenshot = capture_screen()

            # 目标检测
            results = detect_objects(screenshot, model)

            # 获取检测信息
            detection_info = get_detection_info(results)

            # 保存当前检测结果
            last_detection = detection_info

            # 获取自己的位置
            self_center = get_self_center(detection_info['自己'])

            # 合并岩浆和石块作为障碍物
            obstacles = {
                '岩浆': detection_info['岩浆'],
                '石块': detection_info['石块'],
                '怪兽': detection_info['怪兽']
            }

            # 计算FPS
            current_time = time.time()
            fps = 1 / (current_time - last_time)
            last_time = current_time

            # 显示检测结果和FPS
            if results:
                for r in results:
                    im_array = r.plot().copy()  # 直接复制
                    cv2.putText(im_array, f'FPS: {fps:.2f}', (10, 30),
                                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

                    # 显示主方向和模式检测状态
                    if main_direction:
                        cv2.putText(im_array, f'Main Dir: {main_direction}', (10, 60),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
                        cv2.putText(im_array, f'Main Steps: {main_direction_steps}', (10, 90),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)

                    if pattern_detected:
                        cv2.putText(im_array, f'Pattern: {pattern_detected[0]}{pattern_detected[1]}', (10, 120),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
                        cv2.putText(im_array, f'Steps: {pattern_counter}/{PATTERN_MOVE_COUNT}', (10, 150),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)

                    cv2.imshow('YOLOv8 Inference', im_array)
            else:
                # 创建空白帧显示
                im_array = np.zeros((height, width, 3), dtype=np.uint8)
                cv2.putText(im_array, 'No detections', (10, 30),
                            cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
                cv2.imshow('YOLOv8 Inference', im_array)

            # 决策逻辑 - 基于最新检测结果选择移动方向
            move_direction = None
            if self_center:
                move_direction = choose_direction(self_center, obstacles)

            # 执行移动并等待一段时间
            if move_direction:
                move(move_direction)

                # 移动后立即刷新检测
                time.sleep(0.1)  # 短暂等待游戏状态更新
                screenshot = capture_screen()
                results = detect_objects(screenshot, model)
                detection_info = get_detection_info(results)
                last_detection = detection_info
                print("移动后已刷新检测")

            # 控制FPS
            elapsed_time = time.time() - current_time
            if elapsed_time < (1 / TARGET_FPS):
                time.sleep((1 / TARGET_FPS) - elapsed_time)

            # 可根据需要添加退出条件
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

    except KeyboardInterrupt:
        print("程序被用户中断")
    finally:
        # 释放所有按键
        pydirectinput.keyUp(UP)
        pydirectinput.keyUp(DOWN)
        pydirectinput.keyUp(LEFT)
        pydirectinput.keyUp(RIGHT)
        cv2.destroyAllWindows()


if __name__ == "__main__":
    main()
